#ifndef CORO_CPP
#define CORO_CPP

#include "coro.h"
#define MIN_STACK_SIZE 1024*128
#define MAX_STACK_SIZE 1024*1024
#define MIN(a, b) ((a) > (b) ? (b) : (a))
#define MAX(a, b) ((a) < (b) ? (b) : (a))

//帮助运行
void cofunc(schedule_t* schedule) {
    int id = schedule->running_coro;

    if(id<0 && id>schedule->co_cap) return;
    //printf("%d\n",id);
    coro *co = schedule->co[id];
    co->func(co->arg);

    // 标记协程为死亡
    co->status = CO_STATUS_DEAD;
    --schedule->co_numbers;
    
    // 恢复前一个协程s
    coro *pre_co = schedule->co[co->pre_coro];
    pre_co->status = CO_STATUS_RUNNING;
    schedule->running_coro = co->pre_coro;
    
    ucontext_t dummy;
    swapcontext(&dummy, &pre_co->ctx);
}

//打开调度器
schedule_t* sch_open(int stsize){
    schedule_t* sch=(schedule_t*)malloc(sizeof(schedule_t));
    sch->co_numbers=0;
    sch->satck_size=MIN(MAX(stsize,MIN_STACK_SIZE),MAX_STACK_SIZE);
    sch->co_cap=DEFAULT_COROUTINE;
    sch->co=(coro**)malloc(sizeof(coro*)*(sch->co_cap));
    for(int i=0;i<sch->co_cap;++i)  sch->co[i]=NULL;

    int id=coro_create(sch,NULL,NULL);
    assert(id == MAIN_CO_ID);

    coro* co=sch->co[MAIN_CO_ID];
    co->status=CO_STATUS_RUNNING;
    sch->running_coro=id;
    
    return sch;
}

//关闭调度器
void sch_close(schedule_t *schedule){
    assert(schedule->running_coro == MAIN_CO_ID);
    for(int i=0;i<schedule->co_cap;++i){
        coro* co=schedule->co[i];
        if(co){
            free(co->stack);
            free(co);
        }
    }
    free(schedule->co);
    schedule->co=NULL;
    free(schedule);
}

 /*获取在运行的协程id*/
int get_co_running(schedule_t& schedule){
    return schedule.running_coro;
}
/* 创建协程 */
int  coro_create(schedule_t *schedule,co_func _func,void *arg){
    int cid = -1;

    if(schedule->co_numbers >= schedule->co_cap){
        cid=schedule->co_cap;
        schedule->co = (coro**)realloc(schedule->co, schedule->co_cap*2*sizeof(coro*));
        memset(schedule->co + schedule->co_cap, 0, sizeof(coro*)*schedule->co_cap);
        schedule->co_cap*=2;
    }else{
        for(int i=0;i<schedule->co_cap;++i){
            int id=(i+schedule->co_numbers)%(schedule->co_cap);
            if(schedule->co[id]==NULL){
                cid = id;
                break;
            }else if(schedule->co[id]->status==CO_STATUS_DEAD){
                cid=id;
                break;
            }
        }
    }

    if(cid>=0){
        coro* c=NULL;
        if(schedule->co[cid]!=NULL){
            c=schedule->co[cid];
        }else{
            c = (coro*)malloc(sizeof(coro));
            c->pre_coro = MAIN_CO_ID;
            if(cid!=MAIN_CO_ID){
                c->stack = (char*)malloc(schedule->satck_size);
            }
            schedule->co[cid]=c;
        }
        ++(schedule->co_numbers);

        c->func=_func;
        c->arg=arg;
        c->sch=schedule;
        c->status=CO_STATUS_SUSPEND;

        if(_func!=NULL){
            coro* cur_co=schedule->co[schedule->running_coro];
            assert(cur_co>=0);
            
            getcontext(&c->ctx);
            c->ctx.uc_stack.ss_sp=c->stack;
            c->ctx.uc_stack.ss_size=schedule->satck_size;
            c->ctx.uc_link=&(cur_co->ctx);
            c->ctx.uc_stack.ss_flags=0;

            makecontext(&(c->ctx), (void (*)(void))(cofunc),1,schedule);
        }
    }
    return cid;
}
/* 挂起协程 */
void coro_yield(schedule_t &schedule){
    int id = schedule.running_coro;
    if(id == MAIN_CO_ID)    return;

    assert(id>=0);
    coro* co=schedule.co[id];
    coro* pre_co = schedule.co[co->pre_coro];
    co->status=CO_STATUS_SUSPEND;
    pre_co->status=CO_STATUS_RUNNING;

    schedule.running_coro=co->pre_coro;
    swapcontext(&co->ctx,&pre_co->ctx);
}
/* 恢复指定id号的协程运行 */
void coro_resume(schedule_t* schedule,int id){
    assert(id >=0 && id<schedule->co_cap);
    
    coro* co=schedule->co[id];
    coro* cur_co=schedule->co[schedule->running_coro];
    if(co == NULL || cur_co== NULL)  return;

    CORO_STATUS status=co->status;
    switch (status)
    {
    case CO_STATUS_SUSPEND:
        cur_co->status=CO_STATUS_NORMAL;
        co->pre_coro=schedule->running_coro;
        co->status=CO_STATUS_RUNNING;
        schedule->running_coro=id;
        swapcontext(&(cur_co->ctx),&(co->ctx));
        break;
    default:
        return;
    }
}
/* 获取协程的状态 */
enum CORO_STATUS coro_status(schedule_t &schedule, int id){
    assert(id>=0 && id<schedule.co_cap);

    if(schedule.co[id]==NULL)   return CO_STATUS_DEAD;
    return schedule.co[id]->status;
}

#endif